﻿#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>


//操作符

//移位操作符（移动的是二进制位）
//<< 左移操作符
//>> 右移操作符
//注：移位操作符的操作数只能是整数。


//左移操作符
//移位规则：左边抛弃、右边补0
//int main()
//{
//	int a = 10;
//	int b = a << 1;
// //a = a << 1  //a <<= 1
//	//10
//	//000000000000000000000000000001010
//	//
//
//	printf("%d\n", b);//b=20
//	printf("%d\n", a);//a=10
//
//
//	return 0;
//}

//int main()
//{
//	int a = -1;
//	int b = a << 1;
//	//-1
//	//100000000000000000000000000000001     -1的源码
//	//111111111111111111111111111111110     -1的反码
//	//111111111111111111111111111111111     -1的补码
//
//	printf("%b = %d\n", b);//-2
//	printf("a = %d\n", a);//-1
//
//	return 0;
//}




//右移操作符
//移位规则：⾸先右移运算分两种：
//1.逻辑右移：左边⽤0填充，右边丢弃
//2.算术右移：左边⽤原该值的符号位填充，右边丢弃

//右移到底采用算术右移还是逻辑右移是取决于编译器的，通常采用的是算术右移

//int main()
//{
//	int a = -10;
//	//10000000000000000000000000001010     -10的源码
//	//11111111111111111111111111110101     -10的反码
//	//11111111111111111111111111110110     -10的补码
//	int b = a >> 1;
//	printf("a = %d\n", a);
//	printf("b = %d\n", b);//-5
//
//
//	return 0;
//}


//关注真假
//&&   逻辑与
//||   逻辑或



//位操作符：& 、 | 、 ^ 、~       （都只能针对整数进行运算）按二进制位
//&   按位与         - 有0则为0，两个同时为1则为1
//|   按位或         - 只要有1，就是1，两个同时为0，猜为0
//^   按位异或       - 对应的二进制位，相同为0，相异为1
//~   按位取反       - 

//int main()
//{
//	int a = 6;
//	//00000000000000000000000000000110    6的补码
//
//	int b = -7;
//	//11111111111111111111111111111001    -7的补码
//
//	int c = a & b;
//	//进行与运算
//	//00000000000000000000000000000110    6的补码
//	//11111111111111111111111111111001    -7的补码
//	//00000000000000000000000000000000    与运算的结果（补码）
//	printf("c = %d\n", c);//c=0
//
//
//	//按位或
//	//00000000000000000000000000000110    6的补码
//	//11111111111111111111111111111001    -7的补码
//	//11111111111111111111111111111111    或运算的结果
//	int d = a | b;
//	printf("d = %d\n", d);//d=-1
//
//	return 0;
//}



//int main()
//{
//	int a = 0;
//	printf("%d\n", ~a);// ~  按位取反
//
//	return 0;
//}



//⼀道变态的面试题：不能创建临时变量（第三个变量），实现两个整数的交换。
// 方法一
//int main()
//{
//	int a = 3;
//	int b = 5;
//
//
//	printf("交换前：a = %d  b = %d\n", a, b);
//	
//	a = a + b;
//	b = a - b;
//	a = a - b;
//
//	printf("交换后：a = %d  b = %d\n", a, b);
//
//	return 0;
//}


//方法二（异或：支持交换律）
//int main()
//{
//	int a = 3;
//	int b = 5;
//
//
//	printf("交换前：a = %d  b = %d\n", a, b);
//	
//	a = a ^ b;
//	b = a ^ b;
//	a = a ^ b;
//
//	printf("交换后：a = %d  b = %d\n", a, b);
//
//	return 0;
//}




//练习1：编写代码实现：求⼀个整数存储在内存中的⼆进制中1的个数。
//拿到二进制的每一位，然后统计

////方法一
//int count_bit_one(unsigned int n)
//{
//	int count = 0;
//	while (n)
//	{
//		if ((n % 2) == 1)
//			count++;
//
//		n = n / 2;
//	}
//	return count;
//}
//int main()
//{
//	int num = 0;
//	scanf("%d", &num);
//	int ret = count_bit_one(num);
//	printf("%d\n", ret);
//
//
//	return 0;
//}


////方法二
//int count_bit_one(unsigned int n)
//{
//	int i = 0;
//	int count = 0;
//	for (i = 0; i < 32; i++)
//	{
//		if ((n >> i) & 1 == 1)
//		{
//			count++;
//		}
//	}
//	return count;
//}
//int main()
//{
//	int num = 0;
//	scanf("%d", &num);
//	int ret = count_bit_one(num);
//	printf("%d\n", ret);
//
//
//	return 0;
//}




////方法三
////把n的二进制序列中最右边的1去掉
//int count_bit_one(unsigned int n)
//{
//	int i = 0;
//	int count = 0;
//	while (n)
//	{
//		n = n & (n - 1);
//		count++;
//	}
//	return count;
//	
//}
//int main()
//{
//	int num = 0;
//	scanf("%d", &num);
//	int ret = count_bit_one(num);
//	printf("%d\n", ret);
//
//
//	return 0;
//}



////练习：写一个代码，判断n是否是2的次方数
//int main()
//{
//
//
//	return 0;
//}




//练习2：⼆进制位置0或者置1
////编写代码将13⼆进制序列的第5位修改为1，然后再改回0
////                          n
//int main()
//{
//	int a = 13;
//	//00000000000000000000000000001101
//	//00000000000000000000000000010000
//	//
//	//00000000000000000000000000011101
//	//11111111111111111111111111101111
//	//00000000000000000000000000010000
//	//00000000000000000000000000000001
//
//	int n = 5;
//	a = a | (1 << (n - 1));
//	printf("%d\n", a);
//
//	a &= ~(a << (n - 1));
//	printf("%d\n", a);
//
//	return 0;
//}



//在单目操作符
//&  --  取地址操作符
//*  --  解引用操作符



//逗号表达式：用逗号隔开的多个表达式
//逗号表达式，一定要从左向右依次计算，因为前面表达式的计算，可能会影响后面的计算

//int main()
//{
//	int a = 1;
//	int b = 2;
//	int c = (a > b, a = b + 10, a, b = a + 1);//逗号表达式
//
//	printf("%d\n", c);
//
//	return 0;
//}


//int main()
//{
//	int a = 0;
//	int b = 0;
//	int c = 0;
//	int d = 0;
//	if (a = b + 1, c = a / 2, d > 0)//由最后一个表达式决定
//	{
//
//	}
//	return 0;
//}



////[] -- 下标引用操作符
//int main()
//{
//	int arr[10] = { 1,2,3,4,5 };
//
//	int m = arr[4];//数组中下标是4的元素
//
//	//[] -- 下标引用操作符 - 操作数是：3+5，+是操作符，3和5是操作数
//
//	printf("%d\n", m);//5
//
//	return 0;
//}




////函数调⽤操作符：接受⼀个或者多个操作数：第⼀个操作数是函数名，剩余的操作数就是传递给函数的参数。
////函数调用操作符最少有一个操作数（函数名）
//int Add(int x, int y)
//{
//	return x + y;
//}
//int main()
//{
//	printf("hehe\n");// ()  --  函数调用操作符
//	printf("%d\n", 100);
//	int ret = Add(3, 5);//Add 3 5
//
//	return 0;
//}





//结构成员访问操作符
// 
// 结构体
//C语言已经提供了内置类型，如：char、short、int、long、float、double等，但是只有这些内置类型还是不够的，
//假设我想描述学⽣，描述⼀本书，这时单⼀的内置类型是不⾏的。
//描述⼀个学⽣需要名字、年龄、学号、⾝⾼、体重等；
//描述⼀本书需要作者、出版社、定价等。
//C语言为了解决这个问题，增加了结构体这种⾃定义的数据类型，让程序员可以⾃⼰创造适合的类型
// 
//结构是⼀些值的集合，这些值称为成员变量。结构的每个成员可以是不同类型的变量，如：标量、数组、指针，甚⾄是其他结构体。


//struct - 结构体关键字

////学生类型
//struct Student//成员名（自定义）
//{
//	//成员变量（多个就叫成员列表）
//	char name[20];
//	int age;
//	float score;
//}; s4, s5, s6;  //变量列表
//
//struct Student s3 = { "王武",25,88.8 };//全局变量
//
//struct Point
//{
//	int x;
//	int y;
//};
//
//struct S
//{
//	char ch;
//	struct Point p;
//	int arr[10];
//	double d;
//};
//
//int main()
//{
//	int a;
//
//
//	//结构体变量
//	struct Student s1 = { "翠花",20,98.0 };//局部变量
//	struct Student s2 = { "旺财",18,69.8 };
//
//	struct Point p = { 10,20 };
//
//	struct S s = { 'a',{4,5},{1,2,3,4,5,6,7},3.14 };
//
//	return 0;
//}



//结构成员访问操作符  -  .
//结构体成员的直接访问
//结构体成员的直接访问是通过点操作符（.）访问的。点操作符接受两个操作数。


////学生类型
//struct Student//成员名（自定义）
//{
//	//成员变量（多个就叫成员列表）
//	char name[20];
//	int age;
//	float score;
//}; s4, s5, s6;  //变量列表
//
//struct Student s3 = { "王武",25,88.8 };//全局变量
//
//struct Point
//{
//	int x;
//	int y;
//};
//
//struct S
//{
//	char ch;
//	struct Point p;
//	int arr[10];
//	double d;
//};
//
//int main()
//{
//	int a;
//
//
//	//结构体变量
//	struct Student s1 = { "翠花",20,98.0 };//局部变量
//	struct Student s2 = { "旺财",18,69.8 };
//
//	struct Point p = { 10,20 };
//
//	struct S s = { 'a',{4,5},{1,2,3,4,5,6,7},3.14 };
//
//
//
//	//结构成员访问操作符
//	//结构体变量.结构体成员名
//	//-> 这个是依赖指针的，所以后面讲解
//	printf("%c\n", s.ch);
//	printf("坐标是：%d %d\n", s.p.x, s.p.y);
//	printf("%d\n", s.arr[0]);
//	printf("%lf\n", s.d);
//
//
//	return 0;
//}






//操作符的属性：优先级、结合性
//C语言的操作符有2个重要的属性：优先级、结合性，这两个属性决定了表达式求值的计算顺序。

//优先级指的是，如果⼀个表达式包含多个运算符，哪个运算符应该优先执⾏。各种运算符的优先级是不⼀样的。
//相邻的操作符，优先级高的先执行

//以下按照优先级从高到低排列
//• 圆括号（() ）
//• ⾃增运算符（ ++ ），⾃减运算符（ -- ）
//• 单⽬运算符（ + 和 - ）
//• 乘法（ * ），除法（ / ）
//• 加法（ + ），减法（ - ）
//• 关系运算符（ < 、 > 等）
//• 赋值运算符（ = ）


int main()
{
	/*int r = 3 + 4 * 5;
	int s = (3 + 4) * 5;
	printf("%d\n", r);
	printf("%d\n", s);*/


	//相邻的操作符的优先级相同的情况下，结合性说了算
	int r = 3 + 4 + 5;

	return 0;
}

//那请问：当我们明确了优先级和结合性，那是否就能确定一个表达式的计算结果呢?






